/*Ryan Ott
  08371740
  Assignment 5
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "lexer.h"
/*a constructor for a new token*/
struct token* new_Token(union value val, enum lexeme t, int l)
{
	struct token *tok=(struct token*) malloc(sizeof(struct token));
	tok->v=val;
	tok->type=t;
	tok->linenum=l;
	tok->next=NULL;
	tok->prev=NULL;

	return tok;
}

/*a constructor for a new list*/
struct list *new_list()
{
	struct list *ll=(struct list*)malloc(sizeof(struct list));
	ll->head=NULL;
	ll->tail=NULL;
	ll->front=NULL;
	return ll; 
}

/*deletes the token passed*/
void delete_token(struct token *t)
{
	free(t);
}

/*Moves through the list and deletes each token before deleteing itself*/
void delete_list (struct list *ll)
{
	struct token *pos=ll->front;
	struct token *old;
	while (pos!=NULL)
	{
		old=pos;
		pos=pos->next;
		delete_token(old);
	}
	free(ll);
}

/*Adds a token to the end of the list*/
void list_pushback(struct list *ll, struct token *t)
{
	if((ll->head==NULL) || (ll->tail==NULL)) 
		ll->head=ll->tail=t;
	
	else 
		ll->tail=ll->tail->next=t;
	ll->front=ll->head;

}
    
/*Removes a token from the front of the list*/
struct token token_popfront(struct list *ll)
{
	struct token *data = ll->head;
	if(ll->head == NULL) 
                fprintf(stderr, "No more tokens in list\n");
     	else if(ll->head==ll->tail)	
		ll->head=ll->tail=NULL;		
	else if (ll->head != NULL) {
		ll->head = ll->head->next;
		ll->head->prev=NULL;
	}
	return *data;

}

/*creates a token from a string.  This analyzes any potential string and creates a token for it if the data is valid*/
struct token* tokenizer(char *s1, FILE* err, int line)
{
	union value v;
	enum lexeme type;

	if (strcmp(s1, "*") == 0) {		/*Comments*/
	}
	else if(strcmp(s1, "ADD")==0) {		/*OPCODES*/
		v.op=ADD;
		type=OPCODE;
	}
	else if(strcmp(s1, "SUB")==0) {
		v.op=SUB;
		type=OPCODE;
	}
	else if(strcmp(s1, "MULT")==0) {
		v.op=MULT;
		type=OPCODE;
	}	
	else if(strcmp(s1, "DIV")==0){
		v.op=DIV;
		type=OPCODE;
	}
	else if(strcmp(s1, "MOVE")==0) {
		v.op=MOVE;
		type=OPCODE;
	}
	else if(strcmp(s1, "CLR")==0) {
		v.op=CLR;
		type=OPCODE;
	}
	else if(strcmp(s1, "SWAP")==0) {
		v.op=SWAP;
		type=OPCODE;
	}
	else if(strcmp(s1, "JNZ")==0) {
		v.op=JNZ;
		type=OPCODE;
	}
	else if(strcmp(s1, "GET")==0) {
		v.op=GET;
		type=OPCODE;
	}
	else if(strcmp(s1, "PUT")==0) {
		v.op=PUT;
		type=OPCODE;
	}
	else if(strcmp(s1, "JZ")==0) {
		v.op=JZ;
		type=OPCODE;
	}

	
	else if(strcmp(s1, "A")==0) {                      /*Registers*/
		v.r=A;
		type=REGZ;
	}
	else if(strcmp(s1, "R1")==0) {
		v.r=R1;
		type=REGZ;
	}
	else if(strcmp(s1, "R2")==0) {
		v.r=R2;
		type=REGZ;
	}
	else if(strcmp(s1, "R3")==0) {
		v.r=R3;
		type=REGZ;
	}
	else if(strcmp(s1, "R4")==0) {
		v.r=R4;
		type=REGZ;
	}
	else if(strcmp(s1, "R5")==0) {
		v.r=R5;
		type=REGZ;
	}
	else if(strcmp(s1, "R6")==0) {
		v.r=R6;
		type=REGZ;
	}
	else if(strcmp(s1, "R7")==0) {
		v.r=R7;
		type=REGZ;
	}
	else if(strncmp(s1, "-",1)==0&&strchr(s1,'-')==strrchr(s1,'-'))            /* negative numbers*/
	{
		if(strlen(s1)==(strspn(s1,"-0123456789"))&&strlen(s1)>1)	/*ints*/
		{
			v.i=atoi(s1);
			type=INTEGER;
		}
		else if(strchr(s1,'.')==strrchr(s1,'.')&&strlen(s1)==(strspn(s1,"-.0123456789"))&&strlen(s1)>2)   /*floats*/
		{
			v.f=atof(s1);
			type=FLOAT;
		}
		else								/*invalid input with a single negative sign preceding it*/
		{
			fprintf(err,s1);
			fprintf(err," is invalid input from line ");
			fprintf(err,"%d",line);
			fprintf(err,"\n");
			v.i=-1;
			
			return new_Token(v,INTEGER,-1);
		}
	}
	else if(strlen(s1)==(strspn(s1,"0123456789")))				/*positive ints*/
	{
		v.i=atoi(s1);
		type=INTEGER;
	}
	else if(strchr(s1,'.')==strrchr(s1,'.')&&strlen(s1)==strspn(s1,".0123456789")&& strlen(s1)>1)    /*positive floats*/
	{
		v.f=atof(s1);
		type=FLOAT;
	}
	else									/*all other invalid input*/
	{
		fprintf(err,s1);
		fprintf(err," is invalid input from line ");
		fprintf(err,"%d",line);
		fprintf(err,"\n");
		v.i=-1;
		
		return new_Token(v,INTEGER,-1);
	}
	
	return new_Token(v,type,line);						/*returns new tokens with attributes decided upon by if statements*/

}

/*converts a token to a string*/
char* tokToString(struct token *t1, char* sout, char* lexeme,char* val)
{
	if(t1->type==OPCODE)				/*OP CODES*/
	{
		lexeme="OPCODE:";
		if(t1->v.op==ADD)
			val="ADD";
		else if(t1->v.op==SUB)
			val="SUB";
		else if(t1->v.op==MULT)
			val="MULT";
		else if(t1->v.op==DIV)
			val="DIV";
		else if(t1->v.op==MOVE)
			val="MOVE";
		else if(t1->v.op==CLR)
			val="CLR";
		else if(t1->v.op==SWAP)
			val="SWAP";
		else if(t1->v.op==JNZ)
			val="JNZ";
		else if(t1->v.op==JZ)
			val="JZ";
		else if(t1->v.op==GET)
			val="GET";
		else if(t1->v.op==PUT)
			val="PUT";
	}

	else if(t1->type==REGZ)				/*Registers*/
	{
		lexeme="REGISTER:";
		if(t1->v.r==A)
			val="A";
		else if(t1->v.r==R1)
			val="R1";
		else if(t1->v.r==R2)
			val="R2";
		else if(t1->v.r==R3)
			val="R3";
		else if(t1->v.r==R4)
			val="R4";
		else if(t1->v.r==R5)
			val="R5";
		else if(t1->v.r==R6)
			val="R6";
		else if(t1->v.r==R7)
			val="R7";
	}
	else if(t1->type==INTEGER)				/*ints*/
	{
		lexeme="INTEGER:";
		int temp=t1->v.i;
		sprintf(val,"%d",temp);
	}
	else if(t1->type==FLOAT)				/*floats*/
	{
		lexeme="FLOAT:";
		sprintf(val,"%f",t1->v.f);	
	}
	sprintf(sout,"%s %s \n",lexeme,val);			/*prints type of input and value*/
	return sout;
}
void tokOut(struct list *ll, FILE *in, FILE *out, FILE *err)
{
	struct token *pos;
	for(pos=ll->head;pos!=NULL;pos=pos->next)
	{
		char *outStr=(char*)malloc(100);
		char *lexStr=(char*)malloc(10);
		char *valStr=(char*)malloc(80);
		fprintf(out,tokToString(pos,outStr,lexStr,valStr));
		free(outStr);
		free(lexStr);
		free(valStr);
	}
}
/*accepts input file, output file and error file streams.  Seperates input into tokens, prints tokens to output and invalid input to error*/
struct list* lex(FILE* in, FILE* out, FILE* err)
{
	struct list *ll=new_list();
	struct token *test;
	struct token *pos;

	int linenum=1;
	lines=1;
	toknum=0;

	int isEmpty=1;
	char *s;
	char *s2;
	s=(char*)malloc(1000);
	while(fgets(s,1000,in) != NULL)		/*retrieve current line from input stream*/
	{	
		char *result;
		for(result=strtok(s," \t\n"); (result!=NULL) && (strcmp(result, "*")!=0); result=strtok(NULL," \t\n"))   /*seperate by spaces and newline and remove delimiters*/
		{
			test=tokenizer(result,err,lines);	/*used to identify correct and incorrect input*/
			if(test->linenum!=-1){
				list_pushback(ll,test);		/*correct tokens are added to list*/
				toknum++;
			}
			else
				free(test);			/*incorrect tokens are deleted (they are printed to error stream in token creating function*/

		}
		linenum++;
		lines++;
	}
	free(s);
	return ll;
}
int getLines(){
	return lines;
}
	
				
		
		

   

